﻿package ygg.encoding.amf.type.v3
{
	import flash.net.getClassByAlias;
	import flash.utils.ByteArray;
	import flash.utils.*;
	import ygg.encoding.amf.type.*;
	import ygg.encoding.amf.*;
	import ygg.encoding.*;
	import j.sus2maj7.encoding.*;
	
	public class ObjectCodec extends ComplexCodec
	{
		public function ObjectCodec()
		{
			
		}
				/*
		 U29O-ref=U29
; The first (low) bit is a flag
; (representing whether an instance
; follows) with value 0 to imply that
; this is not an instance but a
; reference. The remaining 1 to 28
; significant bits are used to encode an
; object reference index (an integer).

U29O-traits-ref=U29
; The first (low) bit is a flag with
; value 1. The second bit is a flag
; (representing whether a trait
; reference follows) with value 0 to
; imply that this objects traits are
; being sent by reference. The remaining
; 1 to 27 significant bits are used to
; encode a trait reference index (an
; integer).

U29O-traits-ext=U29
; The first (low) bit is a flag with
; value 1. The second bit is a flag with
; value 1. The third bit is a flag with
; value 1. The remaining 1 to 26
; significant bits are not significant
; (the traits member count would always
; be 0).

U29O-traits=U29
; The first (low) bit is a flag with
; value 1. The second bit is a flag with
; value 1. The third bit is a flag with
; value 0. The fourth bit is a flag
; specifying whether the type is
; dynamic. A value of 0 implies not
; dynamic, a value of 1 implies dynamic.
; Dynamic types may have a set of name
; value pairs for dynamic members after
; the sealed member section. The
; remaining 1 to 25 significant bits are
; used to encode the number of sealed
; traits member names that follow after
; the class name (an integer).

class-name=UTF-8-vr
; Note: use the empty string for
; anonymous classes.

dynamic-member=UTF-8-vr value-type
; Another dynamic member follows
; until the string-type is the
; empty string.

object-type=object-marker (U29O-ref | (U29O-traits-ext class-name *(U8)) | U29O-traits-ref | (U29O-traits class-name *(UTF-8-vr))) *(value-type) *(dynamic-member)))
		//*/
		
		override protected function decodeComplex(u:uint, binary:IBinary, context:IContext=null):*
		{
			trace('readObject');
			
			var retVal:Object = { };
			var ctx:AmfContext = AmfContext(context);
			var nameCodec:ICodec = ctx.nameCodec;
			var cFactory:ICodecFactory = ctx.codecFactory;
			var classAlias:String;
			var s:String;
			var traits:Object;
			
			
			if ( u & 1 ) //U29O-traits-ext | U29O-traits
			{
				var bIsDynamic:Boolean = false;
				
				
				u >>>= 1;
				if ( u & 1 ) //U29O-traits-ext
				{
					trace('U29O-traits-ext');
					
					classAlias = nameCodec.decode(binary, context); trace('classname=', classAlias );
					
					var failed:Boolean = false;
					if (classAlias.length > 0)
					{
						var iExtClass:Class;
						var iExt:IExternalizable;
						
						try
						{
							iExtClass = getClassByAlias(classAlias);
							iExt = new iExtClass();
							
							iExt.readExternal(binary.ba);
							
							retVal = iExt;
						}
						catch (er:Error)
						{
							trace('ERROR');
							
							failed = true;
						}
					}
					
					if (failed)
					{
						//indeterminable number of bytes as *(U8).
						//read into byte array until 00?
						
						var bt:ByteArray = new ByteArray();
						var i:int = binary.readUnsignedByte();
						while (i)
						{
							bt.writeByte(i);
							i = binary.readUnsignedByte();
						}
						
						binary.position -= 1;
						
						retVal = bt;
					}
				}
				else //U29O-traits
				{
					trace('U29O-traits');
					
					u >>>= 1;
					bIsDynamic = ( u & 1 ) ? true: false;
					
					classAlias = nameCodec.decode(binary, context); trace('classname=', classAlias );
					
					if (classAlias.length > 0)
					{
						var $class:Class;
						var $instance:*;
						try
						{
							$class = getClassByAlias(classAlias);
							$instance = new $class();
							
							retVal = $instance;
						}
						catch (er:Error)
						{
							trace('ERROR');
						}
					}
					
					u >>>= 1;
					trace(u);
					//value types
					traits = { };
					traits.alias = classAlias.length > 0 ? classAlias : '__anon__';
					traits.props = { };
					
					while (u--)
					{
						s = nameCodec.decode(binary, context);
						traits.props[s] = s;
					}
					trace('traits');
					for (s in traits.props)
					{
						retVal[s] = cFactory.getDecoder(binary.readUnsignedByte(), ctx).decode(binary, context);
						
						trace('name=', s);
						trace('value=', retVal[s]);
					}
					
					
					//dynamics
					
					if (bIsDynamic)
					{trace('dynamics');
						s = nameCodec.decode(binary, context);
						
						while (s.length > 0)
						{
							retVal[s] = cFactory.getDecoder(binary.readUnsignedByte(), ctx).decode(binary, context);
							trace('name=', s);
							trace('value=', retVal[s]);
							s = nameCodec.decode(binary, context);
						}
					}
					
					//push traits
					ctx.addTraits(traits);
				}
			}
			else //U29O-traits-ref
			{
				trace('U29O-traits-ref');
				
				u >>>= 1;
				trace(u);
				traits = ctx.getTraits(u); //might be empty
				trace('traits');
				
				classAlias = traits.alias;
					
				if (classAlias.length > 0 && classAlias != '__anon__')
				{
					var $classr:Class;
					var $instancer:*;
					try
					{
						$classr = getClassByAlias(classAlias);
						$instancer = new $classr();
						
						retVal = $instancer;
					}
					catch (er:Error)
					{
						trace('ERROR');
					}
				}
				
				for (s in traits.props)
				{
					retVal[s] = cFactory.getDecoder(binary.readUnsignedByte(), ctx).decode(binary, context);
					trace('name=', s);
					trace('value=', retVal[s]);
				}
				
				//next byte is 00 when no dyns
				//there is no other way to determine whether there are dynamic properties left than to check the next byte==00
				
				if (binary.readUnsignedByte() > 0)
				{
					trace('dynamics');
					binary.position -= 1;
					
					s = nameCodec.decode(binary, context);
					
					while (s.length > 0)
					{
						retVal[s] = cFactory.getDecoder(binary.readUnsignedByte(), ctx).decode(binary, context);
						trace('name=', s);
						trace('value=', retVal[s]);
						s = nameCodec.decode(binary, context);
					}
				}
				else
				{
					binary.position -= 1;
				}
			}
			
			return retVal;
		}
		
		override protected function encodeComplex(o:Object, b:IBinary, context:IContext=null):void
		{
			trace('writeObject');
			
			var ctx:AmfContext = AmfContext(context);
			var nameCodec:ICodec = ctx.nameCodec;
			var cFactory:ICodecFactory = ctx.codecFactory;
			
			var desc:XML = describeType(o);
			
			var isDynamic:Boolean = (desc.@isDynamic == "true");
			var classAlias:String = desc.@alias;
			
			var isExternal:Boolean = (desc.implementsInterface.(@type == "flash.utils::IExternalizable").length() > 0);
			
			trace(desc, isDynamic, classAlias, isExternal);
			
			//get traits
			var traitsX:XMLList = desc.accessor.(@access == "readwrite");
			var traits:Object = { }; //accessors with read/write
			
			traits.alias = classAlias.length > 0 ? classAlias : '__anon__';
			traits.props = { };
			
			var numTraits:int = 0;
			for each(var k:XML in traitsX)
			{
				traits.props[k.@name] = k.@name;
				numTraits++;
			}
			
			trace('traits len=', numTraits);
			
			var s:String;
			var j:*;
			
			//determine traits-ref vs (U29O-traits-ext | U29O-traits)
			
			var i:int = -1;
			
			i = ctx.indexOfTraits(traits);
			
			trace('indexOfTraits', i);
			
			if (i < 0)
			{
				//determine U29O-traits-ext vs U29O-traits
				
				if (!isExternal)
				{
					//U29O-traits
					trace('U29O-traits');
					
					//write u29 (trait count) x d011
					var u:uint = numTraits << 4 | 3;
					if (isDynamic) u |= 8;
					AmfBinary(b).encodeU29( u );
					
					//write class alias
					
					b.writeBytes( nameCodec.encode(classAlias, ctx).ba, 1 );
					
					//write trait names
					for (s in traits.props)
					{
						b.writeBytes( nameCodec.encode(s, ctx).ba, 1 );
					}
					
					//write trait values
					for (s in traits.props)
					{
						b.writeBytes( cFactory.getEncoder(o[s], ctx).encode(o[s], context).ba );
					}
					
					if (isDynamic)
					{
						//write dynamics
						for (j in o) //only dynamics -> must get traits from describe type
						{
							trace(j);
							b.writeBytes( nameCodec.encode(j, ctx).ba, 1 );
							b.writeBytes( cFactory.getEncoder(o[j], ctx).encode(o[j], context).ba );
						}
					
						b.writeBytes( nameCodec.encode('', ctx).ba, 1 );
					}
					//add traits to context
					ctx.addTraits(traits);
					
				}
				else
				{
					//U29O-traits-ext
					trace('traits-ext');
					
					//write U29 x111
					AmfBinary(b).encodeU29(7);
					
					//write class alias
					b.writeBytes( nameCodec.encode(classAlias, ctx).ba, 1 );
					
					//call writeExternal
					IExternalizable(o).writeExternal(b.ba);
				}
			}
			else
			{
				//traits-ref
				trace('traits-ref');
				
				//traits needs a className!!!!!
				
				traits = ctx.getTraits(i);
				
				//write U29 x01
				AmfBinary(b).encodeU29(i << 2 | 1);
				
				for (s in traits.props)
				{
					b.writeBytes( cFactory.getEncoder(o[s], ctx).encode(o[s], context).ba );
				}
				
				//write dynamics
				trace('writing dynamics');
				trace('isDynamic=', isDynamic);
				
				if (isDynamic)
				{
					for (j in o) //only dynamics -> must get traits from describe type
					{
						trace(j);
						b.writeBytes( nameCodec.encode(j, ctx).ba, 1 );
						b.writeBytes( cFactory.getEncoder(o[j], ctx).encode(o[j], context).ba );
					}
					b.writeBytes( nameCodec.encode('', ctx).ba, 1 );
				}
			}
		}
		override protected function getMarker():uint
		{
			return Amf3Marker.OBJECT;
		}
	}
}